package com.example.naodian.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.naodian.domain.po.EegDataBlock;
import com.example.naodian.mapper.EegDataBlockMapper;
import com.example.naodian.edu.ucsd.sccn.LSL;
import com.example.naodian.edu.ucsd.sccn.LSL.StreamInfo;
import com.example.naodian.edu.ucsd.sccn.LSL.StreamInlet;
import com.example.naodian.edu.ucsd.sccn.LSL.XMLElement;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

import org.springframework.stereotype.Service;

@Service
public class EegDataServiceImpl extends ServiceImpl<EegDataBlockMapper, EegDataBlock> {

    private static final int BATCH_SIZE = 100; // 批处理大小
    private static final AtomicBoolean isRunning = new AtomicBoolean(false); // 标志是否正在运行
    private static final List<EegDataBlock> dataBuffer = new ArrayList<>(); // 数据缓冲区
    private Long recordingId; // 录制ID
    private Thread dataReceivingThread; // 数据接收线程

    public void startReceivingData(Long recordingId) {
        if (isRunning.compareAndSet(false, true)) { // 如果当前未运行，则设置为运行状态
            this.recordingId = recordingId;
            dataReceivingThread = new Thread(() -> {
                try {
                    System.out.println("解析 EEG 流...");
                    StreamInfo[] streams = LSL.resolve_stream("type", "EEG"); // 查找EEG流

                    if (streams.length == 0) { // 如果没有找到EEG流
                        System.out.println("未找到 EEG 流.");
                        return;
                    }

                    // 打开一个数据入口
                    StreamInlet inlet = new StreamInlet(streams[0]);

                    // 获取通道名称
                    XMLElement channelsNode = inlet.info().desc().child("channels");
                    if (channelsNode == null) { // 如果在流描述中找不到 Channels 节点
                        System.out.println("在流描述中找不到 Channels 节点.");
                        return;
                    }

                    int channelCount = inlet.info().channel_count(); // 获取通道数量
                    List<String> channelLabels = new ArrayList<>(); // 存储通道名称

                    XMLElement channelNode = channelsNode.child("channel");
                    if (channelNode == null) { // 如果在通道描述中找不到通道节点
                        System.out.println("在通道描述中找不到通道节点.");
                        return;
                    }

                    for (int i = 0; i < channelCount; i++) { // 遍历所有通道并获取名称
                        channelLabels.add(channelNode.child_value("label"));
                        channelNode = channelNode.next_sibling();
                    }

                    System.out.println("通道名称: " + channelLabels);

                    // 定义感兴趣的通道
                    List<String> selectedChannels = Arrays.asList("P3", "Pz", "P4", "O1", "Oz", "O2");
                    Map<String, Integer> channelIndices = new HashMap<>(); // 存储感兴趣通道的索引

                    // 查找感兴趣通道的索引
                    for (int i = 0; i < channelLabels.size(); i++) {
                        if (selectedChannels.contains(channelLabels.get(i))) {
                            channelIndices.put(channelLabels.get(i), i);
                        }
                    }

                    // 检查是否找到了所有感兴趣的通道
                    if (channelIndices.size() != selectedChannels.size()) {
                        System.out.println("并非所有感兴趣的通道都能找到.");
                        return;
                    }

                    // 接收数据
                    double[] sample = new double[inlet.info().channel_count()];
                    double baseTimestamp = -1.0; // 基准时间戳

                    while (isRunning.get()) { // 当运行标志为true时，持续接收数据
                        double timestamp = inlet.pull_sample(sample); // 获取样本数据
                        if (baseTimestamp == -1.0) { // 初始化基准时间戳
                            baseTimestamp = timestamp;
                        }
                        double elapsedTime = timestamp - baseTimestamp; // 计算经过的时间

                        for (String channel : selectedChannels) { // 遍历感兴趣通道并保存数据
                            EegDataBlock eegDataBlock = new EegDataBlock()
                                    .setRecordingId(recordingId) // 设置录制ID
                                    .setElectrodeId(channelIndices.get(channel).longValue()) // 设置电极ID
                                    .setDataValues(String.valueOf(sample[channelIndices.get(channel)])) // 设置数据值
                                    .setTime(String.valueOf(elapsedTime)) // 设置时间
                                    .setCreatedAt(LocalDateTime.now()) // 设置创建时间
                                    .setUpdatedAt(LocalDateTime.now()) // 设置更新时间
                                    .setElectrodeName(channel); // 设置电极名称

                            dataBuffer.add(eegDataBlock); // 将数据块添加到缓冲区
                        }

                        if (dataBuffer.size() >= BATCH_SIZE) { // 如果缓冲区达到批处理大小，则保存数据
                            saveBatch(dataBuffer);
                            dataBuffer.clear();
                        }
                    }
                } catch (Exception ex) { // 捕获异常并打印堆栈信息
                    ex.printStackTrace();
                } finally { // 确保线程停止后清理资源
                    isRunning.set(false);
                    if (!dataBuffer.isEmpty()) { // 如果缓冲区仍有未保存的数据，则保存
                        saveBatch(dataBuffer);
                        dataBuffer.clear();
                    }
                    System.out.println("数据接收线程已停止.");
                }
            });
            dataReceivingThread.start(); // 启动数据接收线程
        }
    }

    public void stopReceivingData() {
        if (isRunning.compareAndSet(true, false)) { // 如果当前正在运行，则设置为停止状态
            if (dataReceivingThread != null && dataReceivingThread.isAlive()) { // 中断线程
                dataReceivingThread.interrupt();
            }
            if (!dataBuffer.isEmpty()) { // 如果缓冲区仍有未保存的数据，则保存
                saveBatch(dataBuffer);
                dataBuffer.clear();
            }
            System.out.println("数据接收已停止.");
        }
    }

    private void saveBatch(List<EegDataBlock> dataBuffer) {
        saveBatch(dataBuffer, BATCH_SIZE); // 保存一批数据
    }
}
